<template>
    <div style="height: 100%;width: 100%;position: relative;">
      <span
        class="absoluteSpan"
      >{{ region }}</span>
      <el-slider
        v-model="topK"
        vertical
        :max="eventNodeNum"
        class="absoluteSlider"
        height="100px"
        @input="topKSelect"
      />
      <el-button 
        class="button1" 
        icon="el-icon-view" 
        circle
        size="small"
        @click="showAll"/>
      <el-button 
        class="button2"
        icon="el-icon-refresh" 
        circle
        size="small"
        :disabled="showAllFlag"
        @click="graphStateChange"/>
      <div id="neo4jd3"/>
    </div>
</template>

<script>
function rel2DataRelationship(r) {
  return r;
}

function graphNode2DataNode(r) {
  return r;
}

let neo4jd3Instance;
let subGraph, mainGraph;
let allNodes;
let screen_height, screen_width;

function init() {
  const eleId = "neo4jd3";
  document.getElementById(eleId).innerHTML = "";
  let temp_neo4jd3Instance = new Neo4jd3("#neo4jd3", {
    highlight: [],
    red_rel: [],
    icons: {
      天气: 'tint',
      子类: 'database',
      小类: "user",
      大类: 'yelp',
    },
    images: {
      Contributor: "https://eisman.github.io/neo4jd3/img/twemoji/1f38f.svg",
      Project: "https://eisman.github.io/neo4jd3/img/twemoji/1f5c3.svg"
    },
    minCollision: 60,
    neo4jData: {results: [], errors: []},
    nodeRadius: 25,
    // onNodeClick: onNodeClicked,
    onRelationshipDoubleClick: onRelationshipDoubleClicked,
    onNodeDoubleClick: onNodeDoubleClicked,
    onNodeMouseLeave: onNodeMouseLeave,
    zoomFit: true
  });
  screen_height = temp_neo4jd3Instance.get_screen_height();
  screen_width = temp_neo4jd3Instance.get_screen_width();
  return temp_neo4jd3Instance;
}

function setWeight(allGraph) {
  let allRelations = allGraph.relationships;
  let nodesMap = {};
  let w_min = 0x3f3f3f3f, w_max = 0;
  let allNodes = allGraph.nodes;
  allNodes.forEach(node => {
    nodesMap[node.id] = node
  });
  allRelations.forEach(relationship => {
    if (relationship.type === '引发') {
      let tgtIdx = relationship.endNode
      nodesMap[tgtIdx]['weight'] = relationship.showName
      if (nodesMap['a0'].showName !== '天气') {
        nodesMap[tgtIdx].properties['权重'] = relationship.showName
      }
      w_min = Math.min(w_min, relationship.showName)
      w_max = Math.max(w_max, relationship.showName)
    }
  })
  allNodes.forEach(node => {
    if (node.labels[0] === '子类') {
      node.weight = (node.weight - w_min) * 1.0 / (w_max - w_min);
      node.weight *= colorList.length - 1;
      node.weight = Math.floor(node.weight)
    }
  })
}


// function setPosition(allGraph) {
//     let nodeCount = [1,0,0,0];
//     let allNodes = allGraph.nodes;
//     allNodes.forEach(node => {
//       if (node.labels[0] === '子类') {
//         nodeCount[1]++;
//       } else if (node.labels[0] === '小类') {
//         nodeCount[2]++;
//       } else if (node.labels[0] === '大类') {
//         nodeCount[3]++;
//       }
//     });
//     let deltaX = [];
//     let deltaY = screen_height / 5 * 1.5;
//     for (let i = 0; i < 4; ++i) {
//       deltaX[i] = screen_width / (nodeCount[i] + 1) * 2.5;
//     }
//     let nodeCountTemp = [1,1,1,1];
//     allNodes.forEach(node => {
//       let level = node.id.charAt(0).charCodeAt() - 'a'.charCodeAt();
//       node['fx'] = node['x'] = nodeCountTemp[level] * deltaX[level] - screen_height * 1.5;
//       node['fy'] = node['y'] = (level + 1) * deltaY;
//       nodeCountTemp[level]++;
//     })
// }

function setPosition(allGraph) {
  let nodeCount = [1,0,0,0];
  let allNodes = allGraph.nodes;
  let nodesMap = {};
  allNodes.forEach(node => {
    if (node.labels[0] === '子类') {
      nodeCount[1]++;
    } else if (node.labels[0] === '小类') {
      nodeCount[2]++;
    } else if (node.labels[0] === '大类') {
      nodeCount[3]++;
    }
    nodesMap[node.id] = node
  });
  let deltaX = 145;
  let centerX = screen_width / 2;
  let eventNodeNum = nodeCount[1];
  let deltaY = Math.max(eventNodeNum * 4, screen_height / 5 * 1.5);
  let left = centerX - (eventNodeNum - 1) * deltaX / 2.0;
  let right = centerX + (eventNodeNum - 1) * deltaX / 2.0;
  let maxLength = right - left;
  let count = 0;
  // 天气
  nodesMap['a0']['fx'] = nodesMap['a0']['x'] = centerX;
  nodesMap['a0']['fy'] = nodesMap['a0']['y'] = screen_height / 5;
  // 事件
  allNodes.forEach(node => {
    if (node.labels[0] === '子类') {
      nodesMap[node.id]['fx'] = nodesMap[node.id]['x'] = left + count * deltaX;
      nodesMap[node.id]['fy'] = nodesMap[node.id]['y'] = deltaY * 2;
      count ++;
    }
  })
  // 小类 
  deltaX = maxLength / nodeCount[2];
  count = 0;
  left = centerX - (nodeCount[2] - 1) * deltaX / 2.0;
  right = centerX + (nodeCount[2] - 1) * deltaX / 2.0;
  allNodes.forEach(node => {
    if (node.labels[0] === '小类') {
      nodesMap[node.id]['fx'] = nodesMap[node.id]['x'] = left + count * deltaX;
      nodesMap[node.id]['fy'] = nodesMap[node.id]['y'] = deltaY * 3;
      count ++;
    }
  })
  // 大类
  deltaX = maxLength / nodeCount[3];
  count = 0;
  left = centerX - (nodeCount[3] - 1) * deltaX / 2.0;
  right = centerX + (nodeCount[3] - 1) * deltaX / 2.0;
  allNodes.forEach(node => {
    if (node.labels[0] === '大类') {
      nodesMap[node.id]['fx'] = nodesMap[node.id]['x'] = left + count * deltaX;
      nodesMap[node.id]['fy'] = nodesMap[node.id]['y'] = deltaY * 4;
      count ++;
    }
  })
}

function findTopKSubGraph(allGraph, subGraph, topK) {
    let resultGraph = {
      'nodes': [],
      'relationships': [],
    }
    let allNodes = allGraph.nodes, relationships = allGraph.relationships;
    // 初始化一个目标节点到连边的map，便于后续通过事件节点查找连边
    let targetMap = {};
    relationships.forEach(relationship => {
      targetMap[typeof relationship.target === 'string' ? relationship.target : relationship.target.id] = relationship
    })
    // 加入a0节点
    allNodes.forEach(node => {
      if (node.id === 'a0') {
        resultGraph.nodes.push(node)
      }
    })
    // 先把事件节点排在前面，再根据权重排序
    allNodes.sort((n1, n2) => {
      if (n1.id.charAt(0) === 'b' && n2.id.charAt(0) !== 'b') {
        return -1;
      } else if (n1.id.charAt(0) !== 'b' && n2.id.charAt(0) === 'b') {
        return 1;
      } else if (n1.id.charAt(0) === 'b' && n2.id.charAt(0) === 'b') {
        return n2.properties['权重'] - n1.properties['权重'];
      } else {
        return 1;
      }
    });
    for (let i = 0; i < topK; ++i) {
      let topNode = allNodes[i];
      // 加入topNode关联的大类小类节点以及连边
      subGraph[topNode.id].nodes.forEach(node => {
        resultGraph.nodes.push(node);
      })
      subGraph[topNode.id].relationships.forEach(relationship => {
        resultGraph.relationships.push(relationship);
      })
      // 加入topNode节点
      resultGraph.nodes.push(topNode)
      // 加入事件 - 天气连边
      resultGraph.relationships.push(targetMap[topNode.id]);
    }
    return resultGraph;
}

function splitGraph(allGraph) {
    let mainGraph = {
      'predict': {
        'nodes': [],
        'relationships': []
      }
    };
    let subGraph = {};
    let allNodes = allGraph.nodes, allRelationships = allGraph.relationships;

    let badWeather = false;
    allNodes.forEach(node => {
      if (node.id === 'a0' && node.showName !== '天气') {
        badWeather = true;
      }
      if (node.labels[0] === '子类') {
        node.properties['事件'] = node.showName
      } else if (node.labels[0] === '小类' || node.labels[0] === '大类') {
        node.properties['类别'] = node.showName
      }
    })
    allRelationships.forEach(relationship => {
      if (relationship.type === '引发' && badWeather) {
        relationship.properties['权重'] = relationship.showName
      }
    });
    let nodesMap = {};
    let eventNodeFinder = {};
    allNodes.forEach(node => {
      if (node.labels[0] === '子类' || node.labels[0] === '天气') {
        mainGraph.predict.nodes.push(node)
      } 
      if (node.labels[0] === '子类') {
        subGraph[node.id] = {
          'nodes': [],
          'relationships': []
        }
      }
      nodesMap[node.id] = node
    });
    // 记录下每个连边的头尾节点关系，便于从大类节点回溯到事件节点
    allRelationships.forEach(relationship => {
        let sourceNodeId, targetNodeId;
        if (typeof(relationship.source) === 'string') {
            sourceNodeId = relationship.source
            targetNodeId = relationship.target
        } else {
            sourceNodeId = relationship.source.id
            targetNodeId = relationship.target.id
        }
        if (relationship.type === '所属小类' || relationship.type === '所属大类') {
            if (typeof(eventNodeFinder[targetNodeId]) === 'undefined') {
                eventNodeFinder[targetNodeId] = []
            }
            eventNodeFinder[targetNodeId].push(sourceNodeId);
        }
    })
    allRelationships.forEach(relationship => {
      if (relationship.type === '引发') {
        mainGraph.predict.relationships.push(relationship)
      } else {
        let sourceNodeId, targetNodeId;
        if (typeof(relationship.source) === 'string') {
            sourceNodeId = relationship.source
            targetNodeId = relationship.target
        } else {
            sourceNodeId = relationship.source.id
            targetNodeId = relationship.target.id
        }
        let targetNode = nodesMap[targetNodeId];
        if (relationship.type === '所属小类') {
            subGraph[sourceNodeId].relationships.push(relationship)
            subGraph[sourceNodeId].nodes.push(targetNode)
        } else {
            let eventNodeIdList = eventNodeFinder[sourceNodeId]
            eventNodeIdList.forEach(eventNodeId => {
                subGraph[eventNodeId].relationships.push(relationship)
                subGraph[eventNodeId].nodes.push(targetNode)
            })
        }
      }
    });
    return {
        'mainGraph': mainGraph,
        'subGraph': subGraph
    }
}

let colorList = [
  "#C5FFC5",
  "#B3FFB3",
  "#9FFF9F",
  "#8BFF8B",
  "#79FF79",
  "#65ff65",
  "#53FF53",
  "#3FFF3F",
  "#2DFF2D",
  "#19FF19",
  "#05FF05",
  "#00F200",
  "#00DE00",
  "#00CC00",
  "#00B800",
  "#00A400",
  "#009200",
  "#007E00",
  "#006C00",
  "#005800",
]

function onNodeDoubleClicked(node) {
  // let graphData = neo4jd3Instance.get_d3_data()

}

function onNodeMouseLeave(node) {
  edgeColorChange()
}

function onNodeClicked(node) {
  // let graphData = JSON.parse(JSON.stringify(neo4jd3Instance.get_d3_data())) 
  let graphData = neo4jd3Instance.get_d3_data();
  for (let i = 0; i < graphData.relationships.length; i++) {
    const relation = graphData.relationships[i];
    if (relation.source.id === node.id || node.id.charAt(0) !== 'b') {
      return;
    }
  }
  subGraph[node.id].nodes.forEach(node => {
    let exists = false;
    for (let i = 0; i < graphData.nodes.length; ++i) {
      if (graphData.nodes[i].id === node.id) {
        exists = true;
      }
    }
    if (!exists) {
      graphData.nodes.push(node)
    }
  });
  subGraph[node.id].relationships.forEach(relationship => {
    let exists = false;
    for (let i = 0; i < graphData.relationships.length; ++i) {
      if (graphData.relationships[i].id === relationship.id) {
        exists = true;
      }
    }
    if (!exists) {
      graphData.relationships.push(relationship)
    }
  });
  update_data(graphData, 'add')
}

function onRelationshipDoubleClicked(relationship) {

}

function update_data(graph_data, operation) {
  operation === 'remove' ? neo4jd3Instance.removeWithD3Data(graph_data, []) : neo4jd3Instance.replaceWithD3Data(graph_data, []);
  neo4jd3Instance.tick();
  edgeColorChange();
  nodeColorChange();
}

function nodeColorChange() {
  let graphData = neo4jd3Instance.get_d3_data()
  graphData.nodes.forEach(node => {
    if (node.id.charAt(0) !== 'b' || typeof node.properties['权重'] !== 'number') return;
    let t = document.getElementById('node' + node.id);
    let t1 = t.getElementsByClassName('outline')[0];
    let color = colorList[node.weight]
    let darkenColor = d3.rgb(color).darker(1)
    t1.setAttribute('style', `fill: ${color}; stroke: ${darkenColor};`)
  })
}

function edgeColorChange() {
  let graphData = neo4jd3Instance.get_d3_data()
  for (let i = 0; i < graphData.relationships.length; ++i) {
    let relationship = graphData.relationships[i]
    let t = document.getElementById(relationship.id);
    let t1 = t.getElementsByClassName("text")[0];
    let t2 = t.getElementsByClassName("outline")[0];
    if (relationship.type === '所属小类') {
        t1.setAttribute("fill", "#6DCE9E");
        t2.setAttribute("stroke", "#6DCE9E");
    } else if (relationship.type === '所属大类') {
        t1.setAttribute("fill", "#FAAFC2");
        t2.setAttribute("stroke", "#FAAFC2");
    } else {
        t1.setAttribute("fill", "#68BDF6");
        t2.setAttribute("stroke", "#68BDF6");
    }
  }
}
export default {
  name: "wgGraph",
  props: {
    weather_graph_data: {
      default: null
    }
  },
  data() {
    return {
      topK: null,
      selectedWeather: 'predict',
      eventNodeNum: 0,
      showAllFlag: false,
      showAllInfo: '展开',
      curMainGraph: null,
      curAllGraph: null,
      treeFlag: true,
    };
  },
  computed: {
      region() {
          return this.$store.state.region
      },
  },
  mounted() {
    neo4jd3Instance = init();
    this.load_and_show();
  },
  created() {
    subGraph = {};
    mainGraph = {};
    allNodes = [];
    setPosition(this.weather_graph_data[this.selectedWeather]);
    setWeight(this.weather_graph_data[this.selectedWeather]);
    let splitRes = splitGraph(this.weather_graph_data[this.selectedWeather])
    mainGraph = splitRes.mainGraph;
    subGraph = splitRes.subGraph
    this.curMainGraph = mainGraph[this.selectedWeather];
    this.curAllGraph = this.weather_graph_data[this.selectedWeather];
    let count = 0;
    mainGraph[this.selectedWeather].nodes.forEach(node => {
      allNodes.push(node.id)
      if (node.labels[0] === '子类') {
        count++;
      }
    })
    this.eventNodeNum = count;
    this.topK = count;
    // findTopKSubGraph(this.weather_graph_data[this.selectedWeather], subGraph, this.topK);
  },
  methods: {
    graphStateChange() {
      if (this.treeFlag) {
        this.curAllGraph.nodes.forEach(node => {
          node.x = Math.random() * screen_width;
          node.y = Math.random() * screen_height;
          node.fx = null;
          node.fy = null;
        })
        this.curMainGraph = splitGraph(this.curAllGraph).mainGraph[this.selectedWeather];
        neo4jd3Instance.simulation.restart();
        update_data(this.curMainGraph, 'add');
      } else {
        setPosition(this.curAllGraph);
        this.curMainGraph = splitGraph(this.curAllGraph).mainGraph[this.selectedWeather];
        neo4jd3Instance.simulation.stop();
        update_data(this.curMainGraph, 'add');
      }
      this.treeFlag = !this.treeFlag;
    },  
    showAll() {
      this.showAllInfo = this.showAllFlag ? '展开' : '折叠';
      if (this.showAllFlag) {
        update_data(this.curMainGraph, 'remove');
      } else {
        update_data(this.curAllGraph, 'add');
      }
      this.showAllFlag = !this.showAllFlag
    },
    topKSelect(topK) {
      let newGraph = findTopKSubGraph(this.weather_graph_data[this.selectedWeather], subGraph, topK);
      let newMainGraph = splitGraph(newGraph).mainGraph;
      this.curAllGraph = newGraph;
      this.curMainGraph = newMainGraph[this.selectedWeather];
      setPosition(newGraph);
      neo4jd3Instance = init();
      this.showAllFlag = false;
      update_data(this.curMainGraph, 'add')
      this.topK = topK
    },
    load_and_show() {
      setPosition(this.weather_graph_data[this.selectedWeather]);
      setWeight(this.weather_graph_data[this.selectedWeather])
      let splitRes = splitGraph(this.weather_graph_data[this.selectedWeather])
      mainGraph = splitRes.mainGraph;
      subGraph = splitRes.subGraph
      this.curMainGraph = JSON.parse(JSON.stringify(mainGraph[this.selectedWeather]));
      this.curAllGraph = this.weather_graph_data[[this.selectedWeather]]
      let count = 0;
      mainGraph[this.selectedWeather].nodes.forEach(node => {
        allNodes.push(node.id)
        if (node.labels[0] === '子类') {
          count++;
        }
      })
      this.eventNodeNum = count;
      neo4jd3Instance = init();
      update_data(mainGraph[this.selectedWeather], 'add')
    }
  }
};
</script>

<style scoped>
  #neo4jd3 {
    margin-left:20px;
    margin-top:20px;
    margin-right:20px;
    margin-bottom:20px;
    /* width: 90%; */
    height: 100%;
    display: flex;
    background:rgb(132, 220, 255)
    /* background:#fff */
    /* background:#b8b8b8; */
    /* background: rgb(240, 47, 230); */
  }
  .absoluteSlider {
    position: absolute;
    left: 90%;
    top: 5%;
    z-index: 1;
  }
  .button1 {
    position: absolute;
    left: 90.5%;
    top: calc(120px + 5%);
    z-index: 1;
  }
  .button2 {
    position: absolute;
    left: 89.5%;
    top: calc(165px + 5%);
    z-index: 1;
  }
  .absoluteSpan {
    position: absolute;
    left: 85%;
    top: 5%;
    z-index: 1;
    color: #009200;
    font-size: 16;
    font-weight: bold;
  }
</style>